package com.hhkj.captcha.service.biz.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.hhkj.captcha.common.handler.CaptchaHandler;
import com.hhkj.captcha.model.req.SendCaptchaReq;
import com.hhkj.captcha.service.biz.ApiCaptchaBizService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.utils.ip.IpUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * ApiCaptchaBizServiceImpl
 *
 * @author HHKJ-SERVER-ZJ
 * @since 2023/10/25 8:30
 */
@Service
public class ApiCaptchaBizServiceImpl implements ApiCaptchaBizService {

    @Resource
    private RedisCache redisCache;

    @Override
    public void captcha(SendCaptchaReq req, HttpServletRequest request) {
        // 微信小程序过来不需要对验证码进行校验 开关临时处理, 后续不管来源都需要处理
        boolean wxCheckFlag = sourceCheckSwitch(req);
        if (!wxCheckFlag) {
            if (StrUtil.isBlank(req.getCode())) {
                throw new GlobalException("验证码code不能为空");
            }
            if (StrUtil.isBlank(req.getUuid())) {
                throw new GlobalException("验证码唯一id不能为空");
            }
        }
        if (!Validator.isMobile(req.getMobile())) {
            throw new GlobalException("手机号格式错误");
        }
        Map<String, CaptchaHandler> captchaHandlerBeansByNameMap
                = SpringUtil.getBeansOfType(CaptchaHandler.class);
        if (CollectionUtil.isEmpty(captchaHandlerBeansByNameMap)) {
            throw new GlobalException("业务不存在！");
        }

        CaptchaHandler currentCaptchaHandler;
        CaptchaHandler targetCaptchaHandler = null;
        for (Map.Entry<String, CaptchaHandler> captchaHandlerBeansByNameMapEntry
                : captchaHandlerBeansByNameMap.entrySet()) {
            currentCaptchaHandler = captchaHandlerBeansByNameMapEntry.getValue();
            if (req.getSourceCode().equals(currentCaptchaHandler.getCode())) {
                targetCaptchaHandler = currentCaptchaHandler;
            }
        }
        if (targetCaptchaHandler == null) {
            throw new GlobalException("业务不存在！");
        }

        if (!wxCheckFlag) {
            // 发送验证码前比较验证码是否相同
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + Constants.PAGE_NUMBER_VERIFY_CODE + StrUtil.COLON + req.getUuid();
            String captcha = redisCache.getCacheObject(verifyKey);
            redisCache.deleteObject(verifyKey);
            if (!StrUtil.equals(req.getCode(), captcha)) {
                throw new GlobalException("验证码错误！请检查后重试");
            }
        }
        beforeCheckIp(request);
        targetCaptchaHandler.send(req.getMobile());
    }

    /**
     * 检查ip是否被限制
     * @param request
     */
    private void beforeCheckIp(HttpServletRequest request){
        String ipAddr = IpUtils.getIpAddr(request);
        String limitIpCacheKey = CacheConstants.IP_ACCESS_KEY + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        if (redisCache.hasKeyExist(limitIpCacheKey, ipAddr)) {
            Long limitCount = redisCache.getHash(limitIpCacheKey, ipAddr);
            if (limitCount >= Constants.IP_ACCESS_LIMIT_COUNT) {
                throw new GlobalException("请求超过最大限制，请明天再试");
            }
            redisCache.setHashWithExpire(limitIpCacheKey, ipAddr, ++limitCount, 1, TimeUnit.DAYS);
        } else {
            redisCache.setHashWithExpire(limitIpCacheKey, ipAddr, 1L, 1, TimeUnit.DAYS);
        }
    }

    /**
     * 微信小程序 验证码校验判断开关
     * @param req
     */
    private boolean sourceCheckSwitch(SendCaptchaReq req){
        Boolean isWxExist = redisCache.hasKey(CacheConstants.CHECK_CODE_ENABLE_KEY);
        // 关闭此开关表示不校验验证码code, 并且是微信小程序请求
        return Boolean.TRUE.equals(isWxExist) && Constants.WX_SOURCE_CODE.equals(req.getSourceCode());
    }
}
